package com.kuaishou.locallife.open.api.common.utils;


import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import com.kuaishou.locallife.open.api.KsLocalLifeApiException;

/**
 * @author gaojiapei <gaojiapei@kuaishou.com>
 * Created on 2023-02-20
 */
public class AESUtils {

    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

    private static final String KEY_ALGORITHM = "AES";

    private static final String CHARSET = "UTF-8";
    private static final String SPILT = ":";

    /**
     * AES encode
     *
     * @param plainText  明文
     * @param privateKey 消息密钥
     * @return
     * @throws KsLocalLifeApiException
     */
    public static String encode(String plainText, String privateKey) throws KsLocalLifeApiException {
        String iv = genRandomIvParameterSpec();
        String cipherText = encode(plainText, privateKey, iv);
        if (KsStringUtils.isBlank(cipherText)) {
            throw new KsLocalLifeApiException("encode test fail");
        }
        return cipherText + SPILT + iv;
    }

    /**
     * AES decode
     *
     * @param cipherText 密文
     * @param privateKey 消息密钥
     * @return
     * @throws KsLocalLifeApiException
     */
    public static String decode(String cipherText, String privateKey) throws KsLocalLifeApiException {
        if (KsStringUtils.isBlank(cipherText) || KsStringUtils.isBlank(privateKey)) {
            throw new KsLocalLifeApiException("decode param is blank");
        }
        //spilt cipherText and iv
        String[] params = cipherText.split(SPILT);
        if (params.length != 2) {
            throw new KsLocalLifeApiException("cipherText is error");
        }
        return decode(params[0], privateKey, params[1]);
    }

    /**
     * AES encode
     *
     * @param plainText  明文
     * @param privateKey 消息密钥
     * @param iv         初始向量 (经过base64编码后)
     * @return
     */
    public static String encode(String plainText, String privateKey, String iv) throws KsLocalLifeApiException {
        if (KsStringUtils.isBlank(plainText) || KsStringUtils.isBlank(privateKey)) {
            throw new KsLocalLifeApiException("encode param is blank");
        }
        try {
            // cipher init
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            byte[] keyBytes = privateKey.getBytes();
            SecretKey secretKeySpec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
            // iv
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(CHARSET));
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            // encrypt
            byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(CHARSET));

            return Base64.encodeBase64String(encryptedBytes);
        } catch (Exception e) {
            throw new KsLocalLifeApiException("encode test fail", e);
        }
    }

    /**
     * AES decode
     *
     * @param cipherText encode返回值(经过base64编码)
     * @param privateKey 消息密钥
     * @param iv         初始向量（经过base64编码后）
     * @return
     */
    public static String decode(String cipherText, String privateKey, String iv) throws KsLocalLifeApiException {
        if (KsStringUtils.isBlank(cipherText) || KsStringUtils.isBlank(privateKey)) {
            throw new KsLocalLifeApiException("encode param is blank");
        }
        try {
            // cipher init
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            byte[] keyBytes = privateKey.getBytes();
            SecretKey secretKeySpec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
            // iv
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(CHARSET));
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            // base64 decode
            byte[] bytes = Base64.decodeBase64(cipherText);
            // decrypt
            byte[] decryptedBytes = cipher.doFinal(bytes);

            return new String(decryptedBytes, CHARSET);
        } catch (Exception e) {
            throw new KsLocalLifeApiException("decode text fail", e);
        }
    }


    /**
     * generate random ivParameterSpec base64 string (16 bytes)
     *
     * @return
     */
    public static String genRandomIvParameterSpec() {
        byte[] bytes = new byte[12];
        SecureRandom sr = new SecureRandom();
        sr.nextBytes(bytes);

        return Base64.encodeBase64String(bytes);
    }
}
